<template>
    <div class="person">
        <h1>1.1 监视 ref 基本类型</h1>
        <h2>sum = {{ sum }}</h2>
        <button @click="incSum">sum++</button>
        <h2>sum = {{ sum2 }}</h2>
        <button @click="incSum2">sum2++</button>
        <hr>
        <h1>1.2 监视 ref 对象类型</h1>
        <h2>姓名：{{ person.name }}</h2>
        <h2>年龄：{{ person.age }}</h2>
        <button @click="changeName">修改姓名</button>
        <button @click="changeAge">修改年龄</button>
        <button @click="changePerson">修改人</button>
        <hr>
        <h1>2 监视 reactive 对象</h1>
        <h2>车名：{{ car.name }}</h2>
        <h2>车价：{{ car.price }}</h2>
        <button @click="changeCarName">修改车名</button>
        <button @click="changePrice">修改车价</button>
        <button @click="changeCar">修改车</button>
        <hr>
        <h1>3 监视 ref 或 reactive 定义的对象中的某个属性</h1>
        <h2>电脑品牌：{{ computer.brand }}</h2>
        <h2>显卡设备：{{ computer.devices.graphic_card_1 }}, {{ computer.devices.graphic_card_2 }}</h2>
        <button @click="changeComputer">修改电脑品牌</button>
        <button @click="changeG1">修改第一张显卡</button>
        <button @click="changeG2">修改第二张显卡</button>
        <button @click="changeGraphics">修改所有显卡</button>
        <hr>
        <h1>4 监视上述内容的数组（这里以监视鞋的种类和第一双鞋为例）</h1>
        <h2>鞋子种类：{{ shoes.category }}</h2>
        <h2>鞋子位置：{{ shoes.place }}</h2>
        <h2>鞋子：{{ shoes.shoe.brand }}, {{ shoes.shoe.price }}</h2>
        <button @click="changeShoesCategory">修改鞋子种类</button>
        <button @click="changeShoeBrand">修改品牌</button>
        <button @click="changeShoePrice">修改价格</button>
        <button @click="changeShoe">修改鞋子</button>

    </div>
</template>

<script setup lang="ts" name="Persona">
import { reactive, ref, toRef, watch } from 'vue'    // watch 需要引入

/* 
    vue3 中 watch 可以监视的数据有 4 种
    1、ref 定义的
    2、reactive 定义的
    3、函数的返回值（getter）
    4、包含上述内容的数组
*/
 
////////////////////////////// 1.1 ref
let sum = ref(0)
let sum2 = ref(5)

function incSum(){
    sum.value++
}
function incSum2(){
    sum2.value++
}
// 1、情况一、监视 ref 监视 sum 的变化，并且打印新旧 value
watch(sum, (newValue, oldValue)=>{
    console.log(newValue,oldValue)
})
// 停止监视的办法：使用 watch 的返回值函数，该函数执行时就会终止对变量的监视
const stopWatch = watch(sum2, (newValue, oldValue)=>{
    console.log(newValue,oldValue)
    if(newValue > 10){
        stopWatch()
    }
})
//////////////////////////////// 1.2 ref object

let person = ref({
    name: 'zzq',
    age: 28,
    position: 'PF'
})

function changeName(){
    person.value.name += '~'
}
function changeAge(){
    person.value.age++
}
function changePerson(){
    person.value = {
        name:'zm', 
        age:27, 
        position:'none'
    }
}


// watch(person, (newValue, oldValue) => {console.log('person 修改了')})     // 这种情况，watch 只监视 person 对象是否被修改（地址），不关心里面的属性
// 如果想监视对象内部的变化，需要开启深度监视
watch(person, (newValue, oldValue) => {
    console.log(newValue, oldValue)
}, {
    deep: true,      // 这一项使得 watch 可以监视对象内部的深度变化，不仅局限于对象本身的变化
    immediate: true  // 这一项使得原本只在被监视者发生变化时才调用的函数在页面形成时立刻调用一次，不论是否变化。之后的行为不变（也就是添加了第 0 次调用）
})

// 这里有一个很重要的特点，就是 newValue 和 oldValue 是针对 person 对象的，如果 person 对象没有被整体替换，那么新旧两个值就始终是同一个

///////////////////////////////// 2 reactive

let car = reactive({
    name: '比亚迪元plus',
    price: 116800
})
function changeCarName(){
    car.name = '比亚迪秦plus'
}
function changePrice(){
    car.price = 79800
}
function changeCar(){
    Object.assign(car, {
        name: '比亚迪汉EV',
        price: 168000
    }) 
}

// 由于 reactive 构造的响应式对象，替换时无法将对象整个替换，所以 watch 获得的新值和旧值一定是一样的
// reactive 定义的对象，不需要 deep：true 便可以直接监视到对象内部属性的修改
watch(car, (newValue, oldValue) => {
    console.log('car 修改了', newValue == oldValue)
})

///////////////////////////////// 3 attribute in object defined by ref & reactive

let computer = reactive({
    brand: 'RedMi',
    devices: {
        graphic_card_1: 'RTX 3060Ti',
        graphic_card_2: '集成显卡',
    }
})

function changeComputer(){
    computer.brand = 'HUAWEI'
}
function changeG1(){
    computer.devices.graphic_card_1 = 'RTX 4090'
}
function changeG2(){
    computer.devices.graphic_card_2 = 'RTX 4090Ti'
}
function changeGraphics(){
    computer.devices = {
        graphic_card_1: "GTX 2080Ti",
        graphic_card_2: "None"
    }
}
// 3.1 监视对象中的基本类型属性
// 这么写会报错，因为 computer.brand 只是一个普通字符串，不是 ref 或 reactive 包装过的、也不是 getter 或 setter 返回值
// watch(computer.brand, (newValue, oldValue) => {
//     console.log()
// })
// 于是我们要把 computer.brand 包装为一个 getter 函数（也就是让其变成一个函数的返回值），此时新旧值不会重复，因为监视的是属性
watch(() => computer.brand, (newValue, oldValue) => {
    console.log(`computer.brand 改变了`, newValue, oldValue)
})

// 3.2 监视对象中的对象类型属性
// 如果不将对象类型属性包装成 getter 函数，那么在监视时只能监视到该对象类型属性内部的变化，而无法监视对象类型属性本身的变化
// watch(computer.devices, (newValue, oldValue) => {
//     console.log(`computer.devices 改变了`, newValue, oldValue)
// })

// 包装成 getter 函数之后，可以监视到 computer.devices（对象类型属性）的变化，但是无法监视内部变化
// watch(() => computer.devices, (newValue, oldValue) => {
//     console.log(`computer.devices 改变了`, newValue, oldValue)
// })

//// 对于这个问题的理解：监视 reactive 或 ref 对象是在监视对象内部的变化；而监视 getter 函数返回的内容，是在监视返回的内容本身
//// 为了让方法变得完美（也就是既能监视对象内部变化，也能监视对象本身变化），需要下面的写法
watch(() => computer.devices, (newValue, oldValue) => {
    console.log(`computer.devices 改变了`, newValue, oldValue)
}, {deep: true})


// 整体监视 computer
// watch(computer, (newValue, oldValue) => {
//     console.log('电脑整体改变了')
// })


///////////////////////////////// 4 combination of all above
let shoes = reactive({
    category: '篮球鞋',
    place: '玄关',
    shoe:{
        brand: 'LiNing',
        price: 400
    },

})

function changeShoesCategory(){
    shoes.category = '休闲鞋'
}
function changeShoeBrand(){
    shoes.shoe.brand = 'ANTA'
}
function changeShoePrice(){
    shoes.shoe.price = 300
}
function changeShoe(){
    shoes.shoe = {
        brand: 'NIKE',
        price: 500
    }
}

// 同时监视 brand 和 category（基本属性和对象内的属性）
watch([() => shoes.shoe.brand, () => shoes.category], (newValue, oldValue) => {
    console.log(`改变了`, newValue, oldValue)
})

</script>

<style scoped>
.person {
    background-color: #afcfb7;
    border-radius: 10px;
    box-shadow: 0 0 10px;
    padding: 20px;
    margin: 10px 0;
}
button{
    margin-right: 5px;
}
hr{
    border-width: 3px;
    border-color: black;
    border-style: solid;
}
</style>
